#include<iostream>
#include<cstdio>
#include<stdio.h>
#include<unistd.h>
#include<cstdlib>
#include<string>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>


using namespace std;
const int basesize = 1024;
const int envnum = 64;

//我的环境变量表
char* genv[envnum];

//全局的shell工作路径
char pwd[basesize];
char pwdenv[basesize];

const int argvnum = 64;
char* gargv[argvnum];
int gargc = 0;

//重定向
#define NoneRedir   0
#define InputRedir  1
#define OutputRedir 2
#define AppRedir    3

int redir = NoneRedir;
char *filename = nullptr;

int lastcode = 0;

string GetUserName()
{
    string user = getenv("USER");
    return user.empty()?"None":user;
}
string GetHostName()
{
    string hostname = getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}
string GetPwd()
{
    if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwd);
    return pwd;
    //string pwd = getenv("PWD");
    // return pwd.empty()?"None":pwd;
}
string LastDir()
{
    string curr = GetPwd();
    if(curr == "/"||curr == "None") return curr;
    size_t LastPos = curr.rfind("/");
    return curr.substr(LastPos+1);
}
string MakeCommandLine()
{
    char command_line[basesize];
    snprintf(command_line,basesize, "[%s@%s %s]#", GetUserName().c_str(), GetHostName().c_str(), LastDir().c_str());
    return command_line;
}
void PrintCommandLine()
{
    cout<<MakeCommandLine().c_str();
    fflush(stdout);
}


bool GetCommandLine(char* command_buffer, int size)
{
    //get commplate string within space, use "fgets"
    char* result = fgets(command_buffer, size, stdin);
    if(!result) return false;
    command_buffer[strlen(command_buffer)-1] = 0;
    if(strlen(command_buffer)==0) return false;
    return true;
}

#define TrimSpace(pos) do{\
    while(isspace(*pos)){\
        pos++;\
    }\
}while(0)

void PraseCommandLine(char command_buffer[], int len)
{
    (void)len;
    memset(gargv, 0, sizeof(gargv));
    gargc = 0;
    
    redir = NoneRedir;
    filename = nullptr;

    int end = len-1;
   // auto TrimSpace = [](char* pos){do{while(isspace(*pos))pos++;}while(0);};
    while(end >= 0)
    {
        if(command_buffer[end] == '<')
        {
            redir = InputRedir;
            command_buffer[end] = 0;
            filename = &command_buffer[end] + 1;
            TrimSpace(filename);
            break;
        }
        else if(command_buffer[end] == '>')
        {
            if(command_buffer[end-1] == '>')
            {
                redir = AppRedir;
                command_buffer[end] = 0;
                command_buffer[end-1] = 0;
                filename = &command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
            else
            {
                redir = OutputRedir;
                command_buffer[end] = 0;
                filename = &command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
        }
        else
        {
            end--;
        }
    }

    //else  lastcode = 1;

    const char* scp = " ";
    gargv[gargc++] = strtok(command_buffer, scp);
    while((bool)(gargv[gargc++] = strtok(nullptr, scp)));
    gargc--;
}
//void debug()
//{
//    cout<<"argc"<<gargc<<endl;
//    for(int i = 0; gargv[i]; i++)
//    {
//        printf("argv[%d]:%s\n", i, gargv[i]);
//    }
//}
void AddEnv(const char* item)
{
    int index = 0;
    while(genv[index])  index++;
    genv[index] = new char[strlen(item)+1];
    strncpy(genv[index],item,strlen(item)+1);
    genv[++index] = nullptr;
}
bool CheckAndExecuteCommand()
{
    if(strcmp(gargv[0],"cd")==0)  
    {
        if(gargc == 2)
          chdir(gargv[1]); 
        else  lastcode = 1;
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc == 2)
            AddEnv(gargv[1]);
        else  lastcode = 2;
        return true;
    }
    else if(strcmp(gargv[0],"env")==0)
    {
        for(auto& e : genv)
            cout<<e<<"\n";
        lastcode = 0;
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0)
    {
          if(gargc == 2)
          {
              if(gargv[1][0]=='$')
              {
                  if(gargv[1][1]=='?')
                  {
                      cout<<lastcode<<endl;
                      lastcode = 0;
                  }
              }
              else 
              {
                  cout<<gargv[1]<<endl;
                  lastcode = 0;
              }    
          }
          else lastcode = 3;
          return true;
    }
    return false;
}

void DoRedir()
{
    if(redir == InputRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_RDONLY);
            if(fd < 0)  exit(2);
            dup2(fd,0);
        }
        else exit(1);
    }
    else if(redir == OutputRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd < 0)  exit(4);
            dup2(fd,1);
        }
        else exit(3);
    }
    else if(redir == AppRedir)
    {
        if(filename)
        {
            int fd = open(filename, O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd < 0)  exit(6);
            dup2(fd,1);
        }
        else exit(5);
    }
    else
    {
        //empty
    }

}
bool ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0)  return false; //创建失败
    else if(id == 0)
    {
        DoRedir();
        //子进程执行命令
        execvpe(gargv[0],gargv,genv);
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0)
    {
        if(WIFEXITED(status))
        {
            lastcode = WEXITSTATUS(status);
        }
        else  lastcode = 100;
        return true;
    }
    return false;
}

void InitEnv()
{
    extern char** environ;
    int index = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index])+1);
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index] = nullptr;
}
int main()
{
    InitEnv();
    char command_buffer[basesize];
    while(1)
    {
      PrintCommandLine();//命令提示符
      //command_buffer->output
      if( !GetCommandLine(command_buffer, basesize) ) continue;
      //cout<<command_buffer<<'\n';
      PraseCommandLine(command_buffer, strlen(command_buffer));
      //debug();
      if(CheckAndExecuteCommand()) continue;
      ExecuteCommand();
    }
}
